#!/usr/bin/env ruby

require File.dirname(__FILE__) + '/../test_helper'

require 'test/unit'
require 'container/mongrel_controller'

class MongrelControllerTest < Test::Unit::TestCase

  include FileUtils

  def setup
    @sandbox = Sandbox.new
    @controller = MongrelController.new(@sandbox.root)
    # do not _really_ execute any shell commands
    @controller.stubs(:execute)
    @controller.stubs(:chown)
  end

  def teardown
    @sandbox.clean_up
  end

  def test_create_container
    @controller.create_container(['foo', 'bar'], 4000..4001)

    assert File.file?(File.join(@sandbox.root, 'config', 'mongrel.foo.4000.config'))
    assert File.file?(File.join(@sandbox.root, 'config', 'mongrel.foo.4001.config'))
    assert File.file?(File.join(@sandbox.root, 'config', 'mongrel.bar.4000.config'))
    assert File.file?(File.join(@sandbox.root, 'config', 'mongrel.bar.4001.config'))
    assert File.file?(File.join(@sandbox.root, 'config', 'monit.foo.4000.config'))
    assert File.file?(File.join(@sandbox.root, 'config', 'monit.foo.4001.config.reserve'))
    assert File.file?(File.join(@sandbox.root, 'config', 'monit.bar.4000.config'))
    assert File.file?(File.join(@sandbox.root, 'config', 'monit.bar.4001.config.reserve'))
  end

  def test_create_container_should_generate_good_mongrel_rails_config
    @controller.create_container('foo', 4000)

    assert File.file?(File.join(@sandbox.root, 'config', 'mongrel.foo.4000.config'))
    
    config = YAML.load_file(File.join(@sandbox.root, 'config', 'mongrel.foo.4000.config'))
    expected_app_root = File.join(@sandbox.root, 'web', 'rails')

    assert_equal 4000, config[:port]
    assert_equal expected_app_root, config[:cwd]
    assert_equal File.join(expected_app_root, 'public'), config[:docroot]
    
    assert_equal File.join(expected_app_root, 'log', 'mongrel.foo.4000.log'), config[:log_file]
    assert_equal File.join(expected_app_root, 'log', 'mongrel.foo.4000.pid'), config[:pid_file]

    assert_equal 'production', config[:environment]
  end

  def test_start_single_mongrel
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_starting('127.0.0.2:4000')

    @controller.start('127.0.0.2', 4000)
  end

  def test_start_reserve_mongrel
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_starting('127.0.0.2:4001.reserve')

    @controller.start('127.0.0.2', 4001)
  end


  def test_start_two_mongrels_on_the_same_host
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_starting('127.0.0.1:4000')

    @controller.start('127.0.0.1')
  end

  def test_start_everything
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    # only first ports should be started on each host
    set_expectations_for_starting('127.0.0.1:4000', '127.0.0.2:4000')

    @controller.start
  end

  def test_stop_single_mongrel
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_stopping('127.0.0.1:4000')

    @controller.stop('127.0.0.1', 4000)
  end

  def test_stop_two_mongrels_on_the_same_host
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_stopping('127.0.0.2:4000', '127.0.0.2:4001.reserve')

    @controller.stop('127.0.0.2')
  end

  def test_stop_all_mongrels
    @controller.create_container(['127.0.0.1', '127.0.0.2'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_stopping('127.0.0.1:4000', '127.0.0.1:4001.reserve', '127.0.0.2:4000', '127.0.0.2:4001.reserve')

    @controller.stop
  end

  def test_stop_should_not_consider_missing_pid_for_a_reserve_mongrel_as_an_error
    @controller.create_container(['127.0.0.1'], [4000, 4001])

    @controller.stubs(:rails_app_installed?).returns(true)
    set_expectations_for_stopping('127.0.0.1:4000', '127.0.0.1:4001.reserve-skip')

    reserve_mongrel = MongrelInstance.find_one(@controller, :host => '127.0.0.1', :port => 4001)
    assert(!File.exists?(reserve_mongrel.config[:pid_file]))

    assert_nothing_raised { @controller.stop }
  end

  def set_expectations_for_starting(*expected_hosts_and_ports) 
    expected_hosts_and_ports.each do |host_port|
      host, port = host_port.split(':')
      port, reserve = port.split('.')
      expected_working_directory = File.join(@sandbox.root, 'web', 'rails')
      expected_config_path = File.join(@sandbox.root, 'config', "mongrel.#{host}.#{port}.config")
      MongrelInstance.any_instance.expects(:execute).with("cd #{expected_working_directory}; mongrel_rails start --config #{expected_config_path}")
    end
  end

  def set_expectations_for_stopping(*expected_hosts_and_ports)
    fake_pid = '98765'
    expected_hosts_and_ports.each do |host_port|
      host, port = host_port.split(':')
      port, reserve = port.split('.')
      skip = (reserve && reserve =~ /skip$/)
      unless skip
        mongrel = MongrelInstance.find_one(@controller, :host => host, :port => port)
        mkdir_p(File.dirname(mongrel.config[:pid_file]))
        File.open(mongrel.config[:pid_file], 'w') { |f| f.write fake_pid }
        MongrelInstance.any_instance.expects(:execute).with("mongrel_rails stop -c / --pid #{mongrel.config[:pid_file]}")
        fake_pid = fake_pid.next
      end
    end
  end

end
